package com.douban.business;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.core.AbstractConstant;
import com.core.ContentFetcher;
import com.core.DoubanProperties;
import com.core.UserRequest;
import com.douban.constants.DoubanMediaTypeConstant;
import com.douban.constants.DoubanUserActionConstant;


public class DoubanWorker {

	HashMap<String, UserRequest> requestMap = new HashMap<String, UserRequest>();
	Set<String> ongoingUser = new HashSet<String>();
	int maxUser = 5;

	private LinkedList<ContentFetcher> workQueue = new LinkedList<ContentFetcher>();
	
	
	
	static DoubanWorker instance = null;
	
	private DoubanWorker(String propertyFilename){
		try{
			String maxup = DoubanProperties.getInstance(propertyFilename).getProperty("max_num_user");
			if(maxup!=null){
				maxUser = Integer.valueOf(maxup);
			}
		}catch(Exception ex){
			ex.printStackTrace();
		}	
	}
	//test method only
	protected static DoubanWorker getInstance(String propertyFilename){
		if(instance==null){
			instance=new DoubanWorker(propertyFilename);			
		}
		return instance;
	}
	
	public static DoubanWorker getInstance(){
		return getInstance(null);
	}

	
	public void addUserRequest(String username){
		if(requestMap.get(username)==null){
			requestMap.put(username, new DoubanUserRequest(username));
		}
	}

	public void addUserRequest(String username, DoubanMediaTypeConstant[] mediaTypes,
			DoubanUserActionConstant[] actions){
		if(requestMap.get(username)==null){
			requestMap.put(username, new DoubanUserRequest(username,mediaTypes,actions));
		}
	}
	
	protected void breakDownWork(){
		Set<String> usernames = requestMap.keySet();
		Iterator<String> usernameItr = usernames.iterator();
		while (usernameItr.hasNext()) {
			String username = (String) usernameItr.next();
			DoubanUserRequest ur = (DoubanUserRequest)requestMap.get(username);
			synchronized(ongoingUser){
				if (ongoingUser.size()<maxUser && !ongoingUser.contains(username)
						&& !ur.areRequestsAcomplished()) {
					int start = ur.getStart();
					Collection<AbstractConstant[]> reqs = ur.getRequests();
					Iterator<AbstractConstant[]> itrR = reqs.iterator();
					while (itrR.hasNext()) {
						AbstractConstant[] req = (AbstractConstant[]) itrR
								.next();
						DoubanMediaTypeConstant mediaType = null;
						DoubanUserActionConstant userAction = null;
						for (int i = 0; i < req.length; i++) {
							if (req[i] instanceof DoubanMediaTypeConstant) {
								mediaType = (DoubanMediaTypeConstant) req[i];
							}
							if (req[i] instanceof DoubanUserActionConstant) {
								userAction = (DoubanUserActionConstant) req[i];
							}
						}
						if (mediaType != null && userAction != null) {
							workQueue.add(new DoubanContentListFetcher(
									username, mediaType, userAction, start, 0));
							ongoingUser.add(username);
						}
					}

				}
			}
		}
		
	}
	
	public void doWork(){
		synchronized(workQueue){
			Iterator<ContentFetcher> itr = workQueue.iterator();
			while (itr.hasNext()) {
				ContentFetcher fetcher = (ContentFetcher) itr.next();
				new TaskThread(fetcher);
			}
		}
	}
	
	public Collection<String> getOngoingUsers(){
		return ongoingUser;
	}
	
	public Map<String, UserRequest> getRequestsMap(){
		return requestMap;
	}
	
	public int getMaxUser(){
		return maxUser;
	}
	
	public List<ContentFetcher> getWorkQueue(){
		return workQueue;
	}
	
	
	
	protected void addFetcherIntoWorkQueue(ContentFetcher fetcher){
		ongoingUser.add(fetcher.getUserRequest().getUsername());
		workQueue.add(fetcher);
	}
	
	protected void addRequestIntoRequestMap(UserRequest ur){
		requestMap.put(ur.getUsername(), ur);
	}
	
	protected void reset(){
		requestMap.clear();
		ongoingUser.clear();
	}
	
	protected class TaskThread implements Runnable{

		ContentFetcher fetcher;
		
		TaskThread(ContentFetcher f){
			fetcher = f;
			new Thread(this).start();
		}
		
		public void run() {
			try {
				int numberOfLoadedItems = fetcher.loadContent();
				fetcher.calcStart(numberOfLoadedItems);
				
				UserRequest ur = (UserRequest)fetcher.getUserRequest();
				UserRequest dur =	(UserRequest)requestMap.get(ur.getUsername());

				if(numberOfLoadedItems<=0){				
//					fetch work is complete for the user media and action, remove the fetcher from work queue
					synchronized (workQueue) {
//						this request has been done, take this request condition away from user request
//						and both take the fetcher away, since the fetch job is compeleted.
						dur.substractRequest(ur);
						workQueue.remove(fetcher);
					}
					
					Iterator<ContentFetcher> itr = workQueue.iterator();
					while (itr.hasNext()) {
						ContentFetcher f = (ContentFetcher) itr.next();
						if(f.getUserRequest().getUsername()!=null&&f.getUserRequest().getUsername().equals(fetcher.getUserRequest().getUsername())){
							return;
						}						
					}
					//we get here because there is no fetcher from the removed fetcher's username, remove the username from ongoing user set
					synchronized (ongoingUser) {
						ongoingUser.remove(fetcher.getUserRequest().getUsername());
					}
					
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
		
	}

		
}
